﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataAccess = Microsoft.MSE.Catalog.Module.Data;
using DTO = Microsoft.MSE.Catalog.Contracts;
using System.Data.SqlClient;
using System.Data;

namespace Microsoft.MSE.Catalog.Module.Business
{
    public class DataEntityManager
    {
        public DataEntityManager()
        {

        }

        #region Runtime Server  functions

        /// <summary>
        /// This special method is to be used explicitly from SchemaManager for creating entities when 
        /// a new SchemaObject is being created
        /// THIS SHOULD NOT BE CALLED FROM ANYWHERE ELSE
        /// </summary>
        /// <param name="schemaObject"></param>
        /// <param name="txn"></param>
        /// <param name="conn"></param>
        /// <param name="createUpdateSchemaObjects"></param>
        /// <returns></returns>
        public static DTO.DataEntity CreateDataEntityFromSchemaObject(DTO.SchemaObject schemaObject, SqlTransaction txn, SqlConnection conn)
        {
            DTO.DataEntity dataEntity = null;
            try
            {
                dataEntity = new DTO.DataEntity();
                dataEntity.SchemaObjects = new List<DTO.SchemaObject>();
                dataEntity.SchemaObjects.Add(schemaObject);
                dataEntity.Name = DataAccess.DataEntity.BuildDataEntityName(dataEntity);

                DataAccess.DataEntity.CreateDataEntity(dataEntity, txn, conn);

                Guid schemaID = DataAccess.Entity.GetIDFromName(schemaObject.NamespaceUri, DTO.EntityClass.Schema, txn, conn);
                DataAccess.DataEntity.AssociateSchemaObjectToDataEntity(dataEntity.ID, schemaObject.Name, schemaID, txn, conn);

                return dataEntity;
            }
            catch (Exception)
            {
                throw;
            }
        }


        public static DTO.DataEntity CreateDataEntity(DTO.DataEntity dataEntity, SqlTransaction txn, SqlConnection conn, bool createUpdateSchemaObjects)
        {

            bool closeConnection = false;
            bool completeTransaction = false;

            if (conn == null)
            {
                closeConnection = true;
                conn = CatalogSvcHelper.GetConnection();
                conn.Open();
            }
            if (txn == null)
            {
                completeTransaction = true;
                txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Serializable);
            }

            try
            {
                if (createUpdateSchemaObjects == true)
                {
                    List<DTO.SchemaObject> schemaObjects = new List<DTO.SchemaObject>();
                    foreach (DTO.SchemaObject schemaObject in dataEntity.SchemaObjects)
                    {
                        if (schemaObject.Revision != null && schemaObject.Revision.IsLatest == true)
                        {
                            schemaObjects.Add(SchemaManager.CreateOrUpdateSchemaObject(schemaObject, txn, conn));
                        }
                        else
                        {
                            schemaObjects.Add(schemaObject);
                        }
                    }
                    dataEntity.SchemaObjects = schemaObjects;
                }
                // No need to create a data entity explicitly as it is created/updated everytime a SchemaObject is created
                dataEntity.ID = DataAccess.Entity.GetIDFromName(dataEntity.Name, DTO.EntityClass.DataEntity, txn, conn);

                foreach (DTO.SchemaObject schemaObject in dataEntity.SchemaObjects)
                {
                    Guid schemaID = DataAccess.Entity.GetIDFromName(schemaObject.NamespaceUri, DTO.EntityClass.Schema, txn, conn);
                    DataAccess.DataEntity.AssociateSchemaObjectToDataEntity(dataEntity.ID, schemaObject.Name, schemaID, txn, conn);
                }
                if (completeTransaction == true)
                {
                    if (txn != null) txn.Commit();
                }
                if (closeConnection == true)
                {
                    if (conn != null) conn.Close();
                }

                return dataEntity;
            }
            catch (Exception)
            {
                if (completeTransaction == true)
                {
                    if (txn != null) txn.Rollback();
                }
                if (closeConnection == true)
                {
                    if (conn != null) conn.Close();
                }
                throw;
            }
        }

        public static DTO.DataEntity UpdateDataEntity(DTO.DataEntity dataEntity, SqlTransaction txn, SqlConnection conn, bool createUpdateSchemaObjects)
        {
            bool closeConnection = false;
            bool completeTransaction = false;

            if (conn == null)
            {
                closeConnection = true;
                conn = CatalogSvcHelper.GetConnection();
                conn.Open();
            }
            if (txn == null)
            {
                completeTransaction = true;
                txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Serializable);
            }
            try
            {
                if (createUpdateSchemaObjects == true)
                {
                    List<DTO.SchemaObject> schemaObjects = new List<DTO.SchemaObject>();
                    foreach (DTO.SchemaObject schemaObject in dataEntity.SchemaObjects)
                    {
                        if (schemaObject.Revision != null && schemaObject.Revision.IsLatest == true)
                        {
                            schemaObjects.Add(SchemaManager.CreateOrUpdateSchemaObject(schemaObject, txn, conn));
                        }
                        else
                        {
                            schemaObjects.Add(schemaObject);
                        }
                    }
                    dataEntity.SchemaObjects = schemaObjects;
                }
                dataEntity = DataAccess.DataEntity.UpdateDataEntity(dataEntity, txn, conn);
                

                foreach (DTO.SchemaObject schemaObject in dataEntity.SchemaObjects)
                {
                    DataAccess.DataEntity.AssociateSchemaObjectToDataEntity(dataEntity.ID, schemaObject.Name, schemaObject.ID, txn, conn);
                }

                if (completeTransaction == true)
                {
                    if (txn != null) txn.Commit();
                }
                if (closeConnection == true)
                {
                    if (conn != null) conn.Close();
                }
            }
            catch (Exception)
            {
                if (completeTransaction == true)
                {
                    if (txn != null) txn.Rollback();
                }
                if (closeConnection == true)
                {
                    if (conn != null) conn.Close();
                }
                throw;
            }
            return dataEntity;
        }

        public static void DeleteDataEntity(Guid ID)
        {
            DataAccess.DataEntity.DeleteDataEntity(ID, null, null);
        }

        public static DTO.DataEntity GetDataEntity(Guid dataEntityID)
        {
            DTO.DataEntity dataEntity = DataAccess.DataEntity.GetDataEntity(dataEntityID, null, null);
            dataEntity.SchemaObjects = DataAccess.SchemaObject.ListSchemaObjectForDataEntityID(dataEntityID, null, null);
            return dataEntity;
        }

        public static DTO.DataEntityList ListDataEntities()
        {
            return DataAccess.DataEntity.ListDataEntities(null, null);
        }

        #endregion

        #region Runtime Server Group functions

        public static DTO.DataEntityGroup CreateDataEntityGroup(DTO.DataEntityGroup dataEntityGroup)
        {

            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {
                    dataEntityGroup.ID = DataAccess.Entity.CreateEntityGroup(dataEntityGroup.Name, dataEntityGroup.Description, 20, txn, conn);

                    if (dataEntityGroup.DataEntities!= null)
                    {
                        foreach (DTO.DataEntityListElement dataEntity in dataEntityGroup.DataEntities)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(dataEntityGroup.ID, dataEntity.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return dataEntityGroup;
        }

        public static DTO.DataEntityGroup UpdateDataEntityGroup(DTO.DataEntityGroup dataEntityGroup)
        {
            using (SqlConnection conn = CatalogSvcHelper.GetConnection())
            {
                conn.Open();
                SqlTransaction txn = CatalogSvcHelper.GetTransaction(conn, IsolationLevel.Snapshot);

                try
                {

                    DataAccess.Entity.UpdateEntityGroup(dataEntityGroup.ID, dataEntityGroup.Name, dataEntityGroup.Description, txn, conn);


                    List<DTO.DataEntityListElement> oldList = DataAccess.DataEntity.ListDataEntitiesByGroup(dataEntityGroup.ID, txn, conn);
                    /// Delete all items from the old list
                    if (oldList != null)
                    {
                        foreach (DTO.DataEntityListElement oldListElement in oldList)
                        {
                            DataAccess.Entity.RemoveEntityFromEntityGroup(dataEntityGroup.ID, oldListElement.ID, txn, conn);
                        }
                    }
                    /// Add all items from the new list
                    if (dataEntityGroup.DataEntities != null)
                    {
                        foreach (DTO.DataEntityListElement newListElement in dataEntityGroup.DataEntities)
                        {
                            DataAccess.Entity.AssignEntityToEntityGroup(dataEntityGroup.ID, newListElement.ID, txn, conn);
                        }
                    }

                    if (txn != null) txn.Commit(); //Commit transaction
                }
                catch (Exception)
                {
                    if (txn != null) txn.Rollback(); //Rollback transaction
                    throw;
                }
            } //Connection is disposed here

            return dataEntityGroup;
        }

        public static DTO.DataEntityGroup GetDataEntityGroup(Guid ID)
        {
            DTO.DataEntityGroup dataEntityGroup = new DTO.DataEntityGroup();
            DTO.EntityGroup entityGroup = new DTO.EntityGroup();

            entityGroup = DataAccess.Entity.GetEntityGroup(ID, null, null);

            dataEntityGroup.ID = entityGroup.ID;
            dataEntityGroup.Name = entityGroup.Name;
            dataEntityGroup.Description = entityGroup.Description;

            dataEntityGroup.DataEntities = ListDataEntitiesByGroup(ID);

            return dataEntityGroup;
        }

        public static List<DTO.DataEntityListElement> ListDataEntitiesByGroup(Guid entityGroupID)
        {
            return DataAccess.DataEntity.ListDataEntitiesByGroup(entityGroupID, null, null);

        }

        #endregion
    }
}
